package org.biloxi.model;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.biloxi.builders.subjects.cardsubjects.HaveCardSubject;
import org.biloxi.builders.subjects.playersubjects.WhenPlayerSubject;
import org.biloxi.commands.Command;
import org.biloxi.commands.exceptions.CommandOverriddenException;
import org.biloxi.model.card.CardInstance;
import org.biloxi.model.magicobjects.MagicObject;
import org.biloxi.model.player.PlayerState;
import org.biloxi.player.PlayerID;

/**
 * Snapshot description of an instant in a Game.
 * GameState should be just enough information for a player or AI 
 * to make a decision for there turn.
 * 
 * (Historical information should be kept track by player or AI)
 *   
 * @author Peter.WM.Wong@gmail.com
 */
public interface GameState {
	
	//Note, whenever this enum is changed you may need to change NextPhaser.java's constructor!
	public static enum Phase {
		BOT,
		Upkeep, AfterUpKeep, 
		UnTap, AfterUntap, 
		Draw, AfterDraw, 
		Main1, AfterMain1, 
		Combat, AfterCombat, 
		Main2, AfterMain2, 
		EOT;
		
		private static final Map<Phase,Integer> _phaseIndexMap = new HashMap<Phase,Integer>();
		static{
			for(int i=0; i<Phase.values().length; ++i)
				_phaseIndexMap.put(Phase.values()[i], i);
		}

		public static Phase getNextPhase(Phase curPhase){
			return Phase.values()[ (_phaseIndexMap.get(curPhase)+1) % Phase.values().length ];
		}
		public static Phase getPrevPhase(Phase curPhase){
			return Phase.values()[ (_phaseIndexMap.get(curPhase)-1) % Phase.values().length ];
		}
		
	}
	
	public HaveCardSubject getCardSubject(CardInstance c);
	public WhenPlayerSubject getPlayerSubject();
	
	/**
	 * Gets the player id of whoever's turn it is.
	 * @return
	 */
	public PlayerID getTurnsPlayerID();
	
	
	/**
	 * Gets the phase of the game.
	 * @return
	 */
	public Phase getPhase();
	
	/**
	 * @return number of players
	 */
	public int numPlayers();
	
	/**
	 * Convenience method for returning the PlayerState of the player who's turn it is.
	 * @return
	 */
	public PlayerState getTurnsPlayerState();
	
	/**
	 * Gets unmodifiable Map of PlayerStates for all players.
	 * @param id player id
	 * @return PlayerState of player by player id
	 */
	public Map<PlayerID, PlayerState> getPlayerStates();
	
	/**
	 * Gets a list of PlayerIDs.
	 * The order of the PlayerIDs in the list will determine the order of player turns.
	 * @return List of PlayerIDs
	 */
	public List<PlayerID> getPlayerIDs();
	
	
	/**
	 * Convenience method for retrieving PlayerState given a PlayerID.
	 * 
	 * @param pid PlayerID
	 * @return PlayerState or null if PlayerID is doesn't exist.
	 */
	public PlayerState getPlayerState(PlayerID pid);
	
	
	/**
	 * Retrieves the list PlayerIDs that are opponents to the given PlayerID.
	 *  
	 * @param pid
	 * @return
	 */
	public Set<PlayerID> getOppenents(PlayerID pid);
	
	/**
	 * Gets Opponent PlayerStates of a player  
	 * @param id player id
	 * @return PlayerState of player by player id
	 */
	public Map<PlayerID, PlayerState> getOpponentStates(String pid);
	
	public <O,R> R do_(Command<O,R> cmd, O option) throws CommandOverriddenException;
	
	public List<MagicObject> getType(String type, List<MagicObject> mos);
		
}
